import com.sun.xacml.*;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.*;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: tejeddine.mouelhi
 * Date: 10/23/12
 * Time: 2:38 PM
 */
public class MutationOracle {


    public static PDP pdp, pdpMutant;

    static {

        pdp = createPDP("./mutation_analysis/initial_policy/xacml_configuration.xml");

    }

    public static final String REQUESTS_FOLDER = "mutation_analysis/in/Simple_Comb";

    public static void mutate() {


        File testsFiles[], mutantsFiles[], requestFolder, mutantsFolder;
        String expectedResults[], resultTmp, resultInitalPol;
        Collection<String> aliveMutants;
        int index, nbMutants = 0, nbKilled = 0, nbAlive = 0;
        boolean isKilled;
        Map<String, String> mapResults;

        String binaryResultLine = "";

        Map<String, Collection<String>> mapTest2Mutants, mapMutant2Tests;


        // Run first all requests on the initial policy file and store results for next

        requestFolder = new File(REQUESTS_FOLDER);

        testsFiles = requestFolder.listFiles();
        mapResults = new HashMap<String, String>();


        PrintWriter pwMatrix = null;
        try {
            pwMatrix = new PrintWriter(new BufferedWriter
                    (new FileWriter("Matrix.txt")));
        } catch (IOException e) {
            e.printStackTrace();
        }


        index = 0;
        for (File reqFile : testsFiles) {

            resultTmp = retrieveParametersAndEvaluateQuery(reqFile.getAbsolutePath(), pdp);
            mapResults.put(reqFile.getName(), resultTmp);

        }


        // now let's run tests on mutants and check for each response if mutants is killed or not based on expected result


        mutantsFolder = new File("mutation_analysis/mutants");
        mutantsFiles = mutantsFolder.listFiles();
        aliveMutants = new ArrayList<String>();

        int x = 1;


        mapTest2Mutants = new HashMap<String, Collection<String>>();
        mapMutant2Tests = new HashMap<String, Collection<String>>();

        Collection<String> tmpCol;

        for (File mutantFile : mutantsFiles) {


            // skip non XML files
            if (!mutantFile.getName().contains(".xml"))
                continue;

            nbMutants++;

            // if(x == 100)
            //     break;

            // System.out.print("Testing with :" + x++);

            // init PDP with that new mutant policy
            generateXACMLConfigurationFile(mutantFile.getAbsolutePath(), "./mutation_analysis/xacml_configuration.xml");

            pdpMutant = createPDP("./mutation_analysis/xacml_configuration.xml");


            index = 0;
            isKilled = false;
            binaryResultLine = "";
            for (File reqFile : testsFiles) {

                resultTmp = retrieveParametersAndEvaluateQuery(reqFile.getAbsolutePath(), pdpMutant);

                // mutant got killed yahoo!
                resultInitalPol = mapResults.get(reqFile.getName());

                // System.out.println("\n" + resultTmp + " isEqualTo " + resultInitalPol);


                if (!resultTmp.equals(resultInitalPol)) {

                    if (!isKilled)
                        isKilled = true;

                    binaryResultLine += "1 ";
                    System.out.print("1 ");


                    // update mutant to killing tests map
                    if (!mapMutant2Tests.containsKey(mutantFile.getName())) {

                        tmpCol = new ArrayList<String>();
                        tmpCol.add(reqFile.getName());
                        mapMutant2Tests.put(mutantFile.getName(), tmpCol);
                    } else {

                        mapMutant2Tests.get(mutantFile.getName()).add(reqFile.getName());
                    }

                    // update test to killed mutants map
                    if (!mapTest2Mutants.containsKey(reqFile.getName())) {

                        tmpCol = new ArrayList<String>();
                        tmpCol.add(mutantFile.getName());
                        mapTest2Mutants.put(reqFile.getName(), tmpCol);
                    } else {

                        mapTest2Mutants.get(reqFile.getName()).add(mutantFile.getName());
                    }


                } else {

                    binaryResultLine += "0 ";
                    System.out.print("0 ");


                }
            }

            pwMatrix.println(binaryResultLine);
            System.out.println();


            if (!isKilled) {
                aliveMutants.add(mutantFile.getName());
                // System.out.println("   -> Alive! :)");
                nbAlive++;


            } else {
                // System.out.println("   -> Killed :(");
                // System.out.println("   -> Killed! " + resultTmp + " while original policy: " + resultInitalPol);
                nbKilled++;

            }

        }

        pwMatrix.flush();
        pwMatrix.close();


        PrintWriter pwMutantsList = null;
        try {
            pwMutantsList = new PrintWriter(new BufferedWriter
                    (new FileWriter("MutantsList.txt")));


            for (File mutantsFile : mutantsFiles) {
                pwMutantsList.println(mutantsFile.getName());

            }

            pwMutantsList.flush();
            pwMutantsList.close();


        } catch (IOException e) {
            e.printStackTrace();
        }

        PrintWriter pwTestsList;
        try {
            pwTestsList = new PrintWriter(new BufferedWriter
                    (new FileWriter("TestsList.txt")));


            for (File testsFile : testsFiles) {
                pwTestsList.println(testsFile.getName());

            }



            pwTestsList.flush();
            pwTestsList.close();


        } catch (IOException e) {
            e.printStackTrace();
        }




        /*

        System.out.println("Alive Mutants:");

        if(aliveMutants.isEmpty())
            System.out.println("None!!!!!!");

        for (String next : aliveMutants) {
            System.out.println(next);
        }

        float nbK = nbKilled, nbM = nbMutants;


        // print out the two maps
        System.out.println("Test 2 killed mutants Map");
        System.out.println("------------------------------------------------------------------------------");


        for (String next : mapTest2Mutants.keySet()) {
            System.out.print(next + " => " + mapTest2Mutants.get(next).size() + " killed mutants :");

            for (String s : mapTest2Mutants.get(next)) {
                System.out.print(s + " ; ");

            }

            System.out.println();

        }


        System.out.println("Mutant 2 killing tests Map");
        System.out.println("------------------------------------------------------------------------------");


        for (String next : mapMutant2Tests.keySet()) {
            System.out.print(next + " => " + mapMutant2Tests.get(next).size() + " killing tests :");

            for (String s : mapMutant2Tests.get(next)) {
                System.out.print(s + " ; ");

            }

            System.out.println();

        }



        System.out.println("Mutation score is: "+  (nbK / nbM) * 100f + "%" );
        System.out.println(nbKilled + " killed out of :"+  nbMutants + " , " + nbAlive + " still alive mutants");


        */
    }


    public static String retrieveParametersAndEvaluateQuery(String requestFile, PDP pdp) {


        // System.out.println(requestFile);
        String content = OrBAC2XACMLUtils.readFileContent(requestFile);
        int tmpIndex;


        String subBefore = "<Subject><Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:subject:subject-id\" DataType=\"http://www.w3.org/2001/XMLSchema#string\"><AttributeValue>";
        String actBefore = "<Action><Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\" DataType=\"http://www.w3.org/2001/XMLSchema#string\"><AttributeValue>";
        String resBefore = "<Resource><Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\" DataType=\"http://www.w3.org/2001/XMLSchema#string\"><AttributeValue>";
        String contextBefore = "<Environment><Attribute AttributeId=\"current-context\" DataType=\"http://www.w3.org/2001/XMLSchema#string\"><AttributeValue>";


        tmpIndex = content.indexOf(subBefore);
        String subject = content.substring(content.indexOf(subBefore) + subBefore.length(), content.indexOf("<", tmpIndex + subBefore.length()));

        tmpIndex = content.indexOf(actBefore);
        String action = content.substring(content.indexOf(actBefore) + actBefore.length(), content.indexOf("<", tmpIndex + actBefore.length()));

        tmpIndex = content.indexOf(resBefore);
        String resource = content.substring(content.indexOf(resBefore) + resBefore.length(), content.indexOf("<", tmpIndex + resBefore.length()));

        tmpIndex = content.indexOf(contextBefore);
        String context = content.substring(content.indexOf(contextBefore) + contextBefore.length(), content.indexOf("<", tmpIndex + contextBefore.length()));


        String result = null;
        result = evaluateQueryAndGetResult(subject, action, resource, context, pdp);


        // System.out.println("(" + subject + "," + action + "," + resource + "," + context + ") => " + result);


        return result;
    }


    public static String evaluateQueryAndGetResult(String role, String activity, String view, String context, PDP pdp) {

        RequestCtx req = null;
        try {
            req = createRequest(role, activity, view, context);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        // get the response
        ResponseCtx response = pdp.evaluate(req);

        Result res = (Result) response.getResults().iterator().next();

        String result = null;

        if ((res.getDecision() == Result.DECISION_PERMIT))
            result = "Permission";
        else if ((res.getDecision() == Result.DECISION_DENY))
            result = "Prohibition";
        else if ((res.getDecision() == Result.DECISION_INDETERMINATE))
            result = "Indeterminate";
        else if ((res.getDecision() == Result.DECISION_NOT_APPLICABLE))
            result = "Not applicable";

        return result;

    }


    /**
     * Produces a request with given parameters
     *
     * @param role     the rule's role
     * @param activity the activity to use
     * @param view     the view to use
     * @param context  the context to use
     * @return the RequestCtx object
     * @throws java.net.URISyntaxException if any URI exception occurs
     */
    public static RequestCtx createRequest(String role, String activity, String view, String context) throws URISyntaxException {

        // ----- subject
        HashSet attributes = new HashSet();

        // setup the id and value for the requesting subject
        URI subjectId =
                new URI("urn:oasis:names:tc:xacml:1.0:subject:subject-id");

        attributes.add(new Attribute(subjectId, null, null, new StringAttribute(role)));
        // the context
        attributes.add(new Attribute(new URI("current-context"),
                "admin", null,
                new StringAttribute(context)));

        // bundle the attributes in a Subject with the default category
        HashSet subjects = new HashSet();
        subjects.add(new Subject(attributes));

        //------ action
        HashSet action = new HashSet();

        // this is a standard URI that can optionally be used to specify
        // the action being requested
        URI actionId =
                new URI("urn:oasis:names:tc:xacml:1.0:action:action-id");

        // create the action
        action.add(new Attribute(actionId, null, null,
                new StringAttribute(activity)));

        //------ resource

        HashSet resource = new HashSet();

        // the resource being requested

        // create the resource using a standard, required identifier for
        // the resource being requested
        resource.add(new Attribute(new URI(EvaluationCtx.RESOURCE_ID),
                null, null, new StringAttribute(view)));

        // Environment

        HashSet env = new HashSet();

        env.add(new Attribute(new URI("current-context"),
                "admin", null,
                new StringAttribute(context)));


        return new RequestCtx(subjects, resource,
                action, env);
    }


    public static PDP createPDP(String configFile) {

        // load the configuration
        ConfigurationStore store = null;
        try {
            store = new ConfigurationStore(new File(configFile));
        } catch (ParsingException e) {
            e.printStackTrace();
        }

        // use the default factories from the configuration
        store.useDefaultFactories();

        // get the PDP configuration's and setup the PDP
        try {
            return new PDP(store.getDefaultPDPConfig());
        } catch (UnknownIdentifierException e) {
            e.printStackTrace();
        }

        return null;

    }


    // ----------------------------- config file builder

    /**
     * Modifies the XACMl configuration file by putting a new value for the policy file
     *
     * @param xacmlConfigurationFile the XACML configuration file to build
     */
    public static void generateXACMLConfigurationFile(String policy, String xacmlConfigurationFile) {

        String content, line;


        // load the policy configuration file
        try {
            BufferedReader br = new BufferedReader(new FileReader(xacmlConfigurationFile));


            // the content of the file read replace what needed
            content = "";
            line = br.readLine();
            boolean once = true;

            while (line != null) {

                if (line.contains("<string>") && once) {

                    line = "\t<string>" + policy + "</string>\n";

                    once = false;
                    content += line;

                } else {
                    // don't copy <string> again
                    if (!line.contains("<string>"))
                        content += line + "\n";
                }
                line = br.readLine();
            }

            br.close();

            // writing the file
            PrintWriter pw = new PrintWriter(new FileWriter(xacmlConfigurationFile));
            pw.println(content);

            pw.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {

        mutate();


    }


}
